﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using System.Net;
using System.Security.Cryptography.X509Certificates;

namespace XYS.Service.Core
{
    /// <summary>
    /// 端口侦听相关扩展
    /// </summary>
    public static class ListenExtension
    {
        /// <summary>
        /// 添加端口侦听配置
        /// </summary>
        /// <returns></returns>
        public static WebApplicationBuilder AddListen(this WebApplicationBuilder builder)
        {
            //读取 --urls 参数
            var urlsString = builder.Configuration["urls"];
            var certString = builder.Configuration["cert"];
            if (string.IsNullOrEmpty(urlsString))
                urlsString = builder.Configuration["ASPNETCORE_URLS"];

            var gRpcPortString = builder.Configuration["gRpcPort"];
            int gRpcPort = 0;
            int.TryParse(gRpcPortString, out gRpcPort);

            List<string> urls = new();
            if (!string.IsNullOrWhiteSpace(urlsString))
                urls = urlsString.Split(';').Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();

            //如果有 urls 参数 则手动设置 Kestrel 服务器
            if (urls.Count > 0)
            {
                builder.WebHost.UseKestrel(options =>
                {
                    foreach (var url in urls)
                    {
                        var uri = new Uri(url);
                        var ipAddress = uri.Host.ToLower() switch
                        {
                            "localhost" => IPAddress.Loopback,
                            "*" => IPAddress.Any,
                            "0.0.0.0" => IPAddress.Any,
                            _ => IPAddress.Parse(uri.Host)
                        };

                        if (uri.Scheme == "http")
                        {
                            options.Listen(ipAddress, uri.Port, o => o.Protocols = HttpProtocols.Http1AndHttp2);
                            if (gRpcPort > 0)
                                options.Listen(ipAddress, gRpcPort, o => o.Protocols = HttpProtocols.Http2);
                        }
                        else if (uri.Scheme == "https")
                        {
                            Action<ListenOptions> configure = o =>
                            {
                                o.UseHttps(httpsOptions =>
                                {
                                    httpsOptions.AllowAnyClientCertificate();

                                    var certArray = certString.Split(':').Where(x => !string.IsNullOrWhiteSpace(x)).ToArray();
                                    string certPath = certArray[0];
                                    if (File.Exists(certPath))
                                        httpsOptions.ServerCertificate = new X509Certificate2(certPath, certArray.Length > 1 ? string.Join(':', certArray.Skip(1)) : "");
                                });
                            };

                            options.Listen(ipAddress, uri.Port, o =>
                            {
                                configure.Invoke(o);
                                o.Protocols = HttpProtocols.Http1AndHttp2;
                            });
                            if (gRpcPort > 0)
                            {
                                options.Listen(ipAddress, gRpcPort, o =>
                                {
                                    configure.Invoke(o);
                                    o.Protocols = HttpProtocols.Http2;
                                });
                            }
                        }
                    }

                });
            }


            return builder;
        }
    }
}
